package com.aop;


import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 可以用spel表达式替代这种写法
 */
@Component
@Aspect
public class AuditAspect {

    private static final Logger logger = LoggerFactory.getLogger(AuditAspect.class);
    private static final Map<String, Expression> expressionHashMap = new HashMap<>();
    private static final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    private static final String SEPARATE = ":";

    @Autowired
    private InvokeService invokeService;

//    @Pointcut("@annotation(annotation)")
//    public void serviceStatistics(AuditAnnotation annotation){
//
//    }

    @Around("@annotation(com.aop.AuditAnnotation)")
    public Object doAudit(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();

        logger.info("===============enter doAudit pointCut=============");
        // 1.执行业务功能
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        AuditAnnotation auditAnno = methodSignature.getMethod().getAnnotation(AuditAnnotation.class);
        String[] strings = auditAnno.approveContentValue();
        calcaulateKeyExper(joinPoint, strings);


        //查询开关
//        boolean riskControlBizSwitch = appStoreAuditService.queryRiskControlBizSwitch(buildSwitchParam(auditAnno));
        // 判断是否走AOP逻辑
//        if (invokeService.isJobThread()) {
//            // 执行审批后回调方法
//            return joinPoint.proceed();
//        }

        // 2.构建服务对象保存
        doSaveService(joinPoint, methodSignature);

        // 3.返回注解配置的返回值
        Class returnObjectClass = auditAnno.returnObjectClass();
        if (returnObjectClass == void.class || returnObjectClass == Void.class) {
            return null;
        } else if (returnObjectClass == String.class) {
            return auditAnno.returnObjectStr();
        } else {
            return JSON.parseObject(auditAnno.returnObjectStr(), returnObjectClass);
        }
    }

    private void calcaulateKeyExper(ProceedingJoinPoint joinPoint, String[] auditAnno) {
        EvaluationContext evaluationContext = createEvaluationContext(joinPoint);

        for (String s : auditAnno) {
            Expression expression = spelExpressionParser.parseExpression(s);
            Object value = expression.getValue(evaluationContext);
            System.out.println(value);
        }
    }

    private EvaluationContext createEvaluationContext(ProceedingJoinPoint joinPoint) {
        MethodBasedEvaluationContext methodBasedEvaluationContext = new MethodBasedEvaluationContext(
                null,
                getTargetMethod(joinPoint),
                joinPoint.getArgs(),
                parameterNameDiscoverer
        );
        return methodBasedEvaluationContext;
    }

    private Method getTargetMethod(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> aClass = joinPoint.getTarget().getClass();
        Method method = signature.getMethod();
        Method targetMethod = AopUtils.getMostSpecificMethod(method, aClass);
        return targetMethod;
    }


//    private RiskControlBizRecordParam buildSwitchParam(AuditAnnotation auditAnno){
//        RiskControlConstants.BizType bizType = auditAnno.businessType();
//        RiskControlConstants.OperateType operateType = auditAnno.operateType();
//        RiskControlBizRecordParam param = new RiskControlBizRecordParam();
//        if(bizType!=null){
//            param.setBizType(bizType.getCode());
//            if(bizType.getSystemType()!=null){
//                param.setSystemType(bizType.getSystemType().getCode());
//            }
//        }
//        if(operateType!=null){
//            param.setOperateType(operateType.getCode());
//        }
//        return param;
//    }

    private void doSaveService(ProceedingJoinPoint joinPoint, MethodSignature methodSignature) {
        Class<?> aClass = joinPoint.getTarget().getClass();
        // 1.构造服务对象
        Object[] args = joinPoint.getArgs();
        List<ParamObj> paramList = new ArrayList<>();
        Class[] parameterTypes = methodSignature.getParameterTypes();
        if (parameterTypes != null) {
            for (int i = 0; i < parameterTypes.length; i++) {
                Class parameterType = parameterTypes[i];
                ParamObj paramObj = new ParamObj();
                paramObj.setClassName(parameterType.getName());
                paramObj.setJsonValue(JSON.toJSONString(args[i]));
                paramList.add(paramObj);
            }
        }

        AuditAnnotation auditAnno = AnnotationUtils.findAnnotation(methodSignature.getMethod(), AuditAnnotation.class);
        //获取类上面的注解
//        AuditAnnotation a = AnnotationUtils.findAnnotation(joinPoint.getTarget().getClass(), AuditAnnotation.class);

//        AuditAnnotation auditAnno = methodSignature.getMethod().getAnnotation(AuditAnnotation.class);
        Map<String, Object> fieldNameAndValue = ReflectUtil.getFieldNameAndValue(joinPoint);

        // 2.去重
        RiskControlConstants.BizType businessType = auditAnno.businessType();
        String[] businessKeyArray = auditAnno.businessKey();
        StringBuilder businessUniqueKey = new StringBuilder();
        if (businessType != null && ArrayUtils.isNotEmpty(businessKeyArray)) {
            for (String s : businessKeyArray) {
                businessUniqueKey.append(ReflectUtil.getValueStr(s, fieldNameAndValue));
                businessUniqueKey.append(SEPARATE);
            }
            businessUniqueKey.append(businessType.getCode());

            logger.info("AuditAspect businessUniqueKey:{}", businessUniqueKey);
        }

        RiskControlBizRecord data = new RiskControlBizRecord();
        data.setClassName(methodSignature.getDeclaringTypeName());
        data.setMethodName(methodSignature.getName());
        data.setBizData(JSON.toJSONString(paramList));
        data.setBizDataKey(DigestUtils.md5Hex(data.getBizData()));
        data.setBusinessUniqueKey(businessUniqueKey.toString());
        data.setBusinessUniqueKeyHash(DigestUtils.md5Hex(businessUniqueKey.toString()));
        data.setBizType(auditAnno.businessType().getCode());
        data.setSystemType(auditAnno.businessType().getSystemType().getCode());
        data.setOperateType(auditAnno.operateType().getCode());
        data.setCreator(ReflectUtil.getValueStr(auditAnno.operator(), fieldNameAndValue));
        data.setCreateTime(System.currentTimeMillis());
        data.setTaskStatus(RiskControlConstants.TaskStatus.IN_APPROVAL.getCode());
        data.setBizStatus(RiskControlConstants.BizStatus.TOBE_AUDITED.getCode());
        data.setBizResult(RiskControlConstants.BizResult.UNDO.getCode());
        data.setTaskName(ReflectUtil.getValueStr(auditAnno.taskName(), fieldNameAndValue));

        String[] keys = auditAnno.approveFormId();

        String[] values = auditAnno.approveContentValue();

        Map<String, Object> formDataMap = new HashMap<>();
        for (int i = 0, j = keys.length; i < j; i++) {
            formDataMap.put(keys[i], ReflectUtil.getValue(values[i], fieldNameAndValue));
        }

        data.setFormData(JSON.toJSONString(formDataMap));

        //判断是否在进行中，保存到数据库， 发起审批
//        int code = appStoreAuditService.storeRiskControlBizRecord(data);
//        if(code!=RiskControlConstants.SUCCESS){
//            String message = RiskControlConstants.ExceptionMsg.fromCode(code).getMessage();
//            throw new RuntimeException(message);
//        }
    }

}
